#include "Tipos.h"
#include "modulos/aed2/Lista.h"
#include "modulos/aed2/Arreglo.h"
#include "ArbolCategorias.h"
#include "LinkLinkIt.h"

LinkLinkIt::LinkLinkIt( const ArbolCategorias &ac ) 
	: cats( ac.CantCategorias() + 1 ), ac( ac )
{
	ArbolCategorias::Iterador it( ac );
	version = 1;
	fechaActual = 0;
	cats.Definir( 1, new nodoCat() );

	while ( it.HayMas() ) {
		AgregarCategoria( it.Siguiente() );
		AgregarHijos( it );
		it.Avanzar();
	}
}

void LinkLinkIt::AgregarCategoria( const ArbolCategorias::Iterador::ItElem &e ) {
	nodoCat *nc = new nodoCat();
	if ( e.tienePadre )
		nc->padre = cats[ e.padreId ];
	cats.Definir( e.id, nc );
}

void LinkLinkIt::AgregarHijos( const ArbolCategorias::Iterador &it ) {
	ArbolCategorias::Iterador hijos( ac, it );
	while ( hijos.HayMas() ) {
		AgregarCategoria( hijos.Siguiente() );
		AgregarHijos( hijos );
		hijos.Avanzar();
	}
}

LinkLinkIt::~LinkLinkIt() {
	int n = cats.Tamanho();
	links.EstablecerDestructor( __liberar_dicctrie );
	for ( int i = 0; i <= n; i++ ) {
		if ( cats.Definido( i ) )
			delete cats[i];
	}
}

void LinkLinkIt::NuevoLink( const String &link, const String &cat ) {
	nodoLink *nl = new nodoLink( link, cat );
	nodoCat *nc;

	version++;
	links.Definir( link, nl );

	nc = cats[ac.Id(cat)];
	nl->categoria = nc;
	while ( nc != NULL ) {
		nc->total++;
		nc->links.AgregarAtras( nl );
		nc = nc->padre;
	}
}

void LinkLinkIt::Acceso( const String &link, Nat fecha ) {
	Nat idx;
	nodoLink *nl = links.Obtener( link );
	version++;
	fechaActual = fecha;
	nl->categoria->ultimaFecha = fecha; // pre: fecha >= ultimaFecha(linklinkit)
	idx = SeleccionarFechaAModificar( nl, fecha );

	if ( nl->accesos[idx].valido && nl->accesos[idx].fecha == fecha ) {
		nl->accesos[idx].accesos++;
	} else {
		nl->accesos[idx].fecha = fecha;
		nl->accesos[idx].accesos = 1;
		nl->accesos[idx].valido = true;
	}

	// Quitamos las fechas que quedaron obsoletas (<fecha-3)
	for ( int i = 0; i < 3; i++ ) {
		if ( nl->accesos[i].fecha + 3 <= fecha )
			nl->accesos[i].valido = false;
	}
}

Nat LinkLinkIt::SeleccionarFechaAModificar( const nodoLink *nl, Nat fecha ) {
	Nat n, idx;
	idx = 3;
	for ( n = 0; n < 3; n++ ) {
		if ( nl->accesos[n].valido && nl->accesos[n].fecha == fecha )
			idx = n;
	}

	if ( idx > 2 ) {
		for ( n = 0; n < 3; n++ ) {
			if ( !nl->accesos[n].valido ) {
				idx = n;
				n = 3;
			} else if ( idx > 2 || nl->accesos[n].fecha < nl->accesos[idx].fecha ) {
				idx = n;
			}
		}
	}

	return idx;
}

Nat LinkLinkIt::CantLinks( const String &cat ) {
	return cats[ac.Id(cat)]->total;
}

LinkLinkIt::Iterador::Iterador( const LinkLinkIt &lli, const String &categoria ) {
	nodoCat *nc = lli.cats[lli.ac.Id(categoria)];
	ultimaFecha = nc->ultimaFecha;

	if ( nc->version != lli.version ) {
		Sort( nc->links );
		nc->version = lli.version;
	}

	lista = nc->links.CrearIt();
}

Nat LinkLinkIt::Iterador::CalcularAccesosRecientes( const accestr fl[3] ) const {
	Nat res = 0;
	for ( int i = 0; i < 3; i++ ) {
		if ( fl[i].valido && (fl[i].fecha <= ultimaFecha && fl[i].fecha + 3 > ultimaFecha) )
			 res += fl[i].accesos;
	}
	return res;
}

void LinkLinkIt::Iterador::Sort( aed2::Lista<nodoLink*> &lista ) const {
	Nat i, n, newn;
	aed2::Lista<nodoLink *>::Iterador it;
	nodoLink *p1, *p2;

	n = lista.Longitud();
	if ( n < 2 ) return;

	while ( n > 0 ) {
		i = newn = 0;
		it = lista.CrearIt();
		it.Avanzar();
		while ( i < n && it.HaySiguiente() ) {
			p1 = it.Anterior();
			p2 = it.Siguiente();
			if ( CalcularAccesosRecientes( p1->accesos ) <
				CalcularAccesosRecientes( p2->accesos ) ) {
				it.EliminarSiguiente();
				it.EliminarAnterior();
				it.AgregarComoAnterior( p2 );
				it.AgregarComoSiguiente( p1 );
				newn = i;
			}
			i++;
			it.Avanzar();
		}
		n = newn;
	}
}

LinkLinkIt::Iterador::ItElem LinkLinkIt::Iterador::Siguiente() const {
	nodoLink *nl = lista.Siguiente();
	return ItElem(
		nl->nombre,
		nl->cat,
		CalcularAccesosRecientes( nl->accesos )
	);
}
